function SpTreeView() {
  var _hashParameter = Library.WebControl.Hashtable.CreateHashtable();
  var _expenedClass = "tvexpend";
  var _collapseClass = "tvcollapse";
  var _effectTime;
  var _effectStep;
  var _effectDisplay;
  var _instance = this;
  var _util = Library.WebControl.Util;
  var _id;
  var _expendedCallbackType = "3";
  var _customCallbackType = "1";
  var _dataCallback = "2";
  var _clientName;
  var _callbackInExpend;
  var _beforeExpended = null;
  var _beforeCallback = null;
  this.Init = function(clientName, id, effectDisplay, effectTime, effectStep, callback) {
    _clientName = clientName;
    _globalHashObject.Add(clientName, _instance);
    window[clientName] = _instance;
    _id = id;
    _effectDisplay = effectDisplay;
    _effectStep = effectTime;
    _effectTime = effectTime;
    _callbackInExpend = callback;
  };
  function getLiFromSpan(span) {
    return _util.GetObjectById(span).FindParent("div", "tvitem");  //span.parentNode.parentNode;
  };
  function commonCallback(parameter, callbackType, objectCallback, functionCallback) {
		if (_beforeCallback != null)
			_beforeCallback(_instance, {});
    if (!functionCallback || typeof (functionCallback) != "function") {
      functionCallback = function(s, e) {
      };
    }
    var ajaxRequest = Library.WebControl.Ajax.CreateRequest();
    ajaxRequest.SetData("__SPTREEVIEWF_=" + callbackType + '&__SPTREEVIEWSID_=' + _id + '&__SPTREEVIEWRQP_=' + encodeURIComponent(parameter) + '&' + _hashParameter.ToQuery());
    _hashParameter.Clear();
    ajaxRequest.SendCallback({
      T: callbackType,
      P: parameter,
      F: functionCallback,
      O: objectCallback
    }, callbackComplete);
  };
  function callbackComplete(s, e) {
    var argsResult = _util.ParseAjaxResponse(e.ResponseText);
    argsResult.Parameter = s.P;
    argsResult.CallbackType = s.T;
    if (!argsResult.IsValid) {
      alert(argsResult.Result);
    }
    else {
      if (s.T == _expendedCallbackType) {
       var li = s.O.Li;
				var node = li.firstChild;
				while(node != null) {
					var next = node.nextSibling;
					//if (node.nodeName.toLocaleLowerCase() != "div") {
					//	li.removeChild(node);
					//}
					if (_util.GetClass(node) != "tvtitle")
					  li.removeChild(node);
					node = next;
				}
      
              
        s.O.Sp.setAttribute("onclick", _clientName + ".Collapse(this);");
        _util.SetClass(s.O.Sp, _expenedClass);
        var divDummy = document.createElement("div");
        _util.EvalScriptAndHtml(argsResult.Data, divDummy);
        while (divDummy.firstChild) {
          s.O.Li.appendChild(divDummy.firstChild);
        }
      }
      else
        if (s.T == _customCallbackType) {
        _util.EvalScriptAndHtml(argsResult.Data, _id);

      }
    }
    s.F(_instance, argsResult);
  };
  this.Collapse = function(sp) {
    var li = getLiFromSpan(sp);
    var divTreeView = li.FindLastChild("div", "sptreeview");
    if (divTreeView != null) {
      Library.WebControl.Effect.Hide({
        Object: divTreeView,
        Type: _effectDisplay,
        Time: _effectTime,
        Step: _effectStep
      });
    }    

    sp.setAttribute("onclick", _clientName + ".Expend(this);");
    sp.onclick = function() {
      _instance.Expend(sp);
    };

    _util.SetClass(sp, _collapseClass);

  };
  function parseChildNode(ul, f) {
    var nodes = new Array();
    var ul = _util.GetObjectById(ul);  
    ul.Foreach(function(s, e) {
      if (!f(s)) {
        e.Break = true;
      } else {
        else nodes.push(n);
      }
    });		
		return nodes;
  };
  this.IsExpended=function(n) {
   
		var d = _util.FindFirstChild(n, "div");		
		return _util.FindFirstChild(d, "div", _expenedClass) != null;	
		
  };
  
  this.RefreshSubTree = function(key) {
		if (key == null) return;
		var li = null;
		if (typeof(key) == "string") li = this.FindNodeByKey(key);
		else {
			li = key;
		}
		if (li != null) {
			var divTitle = null;
      var child = li.firstChild;        
      while (child != null) {
        if (child.tagName.toLocaleLowerCase() == "div") {
          divTitle = child;
          child = child.nextSibling;
        } else {
          var next = child.nextSibling;
          li.removeChild(child);
          child = next;
        }
      }
      if (divTitle != null)
        child = divTitle.firstChild;
      while (child != null) {
        if (child.tagName.toLocaleLowerCase() == "div") {
					setTimeout( function() { _instance.Expend(child);}, 20);
          break;
        }
        child = child.nextSibling;
      }
		}		
  };
  this.FindNodeByKey = function(k) {
		if (typeof(k) != "string") return null;
		var obj = _util.GetObjectById(_id);
		var node = null;    
    var childs = parseChildNode(obj, function(n) {
			if(_instance.GetKeyFromNode(n) == k) {
				node = n;
				return false;
			}
			return true;
     });
    if (node!= null) return node;
    return findNode( childs, k);   
  };
  this.FindNodesByLevel=function(l) {
		if(l == null) return new Array();
		l = parseInt(l.toString());
		if (isNaN(l)) return new  Array();
		var obj = _util.GetObjectById(_id);
		var childs = parseChildNode(obj, function(n) {return true;});
		return findNsByLv(l, childs);
  };
  function findNsByLv (l, ns){
		var arrs = new Array();
		if (ns.length == 0) arrs;
		var lv = _instance.GetLevelFromNode(ns[0]);
		lv = parseInt(lv);
		if (lv == l) return ns;
		
		for (var i=0;i<ns.length; i++) {
			var n = ns[i];
			var sub = findSubTree(n);
			if (sub != null) {
				var childs = parseChildNode(sub, function(p) {return true;});
				var cl = findNsByLv(l, childs);
				for (var j =0;j<cl.length; j++){
					arrs.push(cl[j]);
				}
			}
		}
		return arrs;
  };
  function findNode(lis, k) {
		for (var i=0;i<lis.length; i++) {
			var li = lis[i];
			var ul = findSubTree(li);
			if (ul != null){
				var node = null;
				var childLis = parseChildNode(ul, function(n) {
				if(_instance.GetKeyFromNode(n) == k) {
					node = n;
					return false;
				}
				return true;
			 });
			 if (node != null) return node;
			 node = findNode(childLis, k);
			 if(node != null) return node;
			}
		}
		
		return null;
  };
  this.GetKeyFromNode= function (n) {
		if (n!= null) {
			if (n.getAttribute) {
				return n.getAttribute("sptnk");
			}
		}
		return "";
  };
  this.GetLevelFromNode=function(n) {
		if (n!= null) {
			if (n.getAttribute) {
				return n.getAttribute("sptnl");
			}
		}
		return "";
  };
  function findSubTree(li) {		
		return _util.FindLastChild(li, "div", "sptreeview");		
  };
  this.SendCallback = function(parameter, callbackFunction) {
    commonCallback(parameter, _customCallbackType, {}, callbackFunction);
  };
  this.SendDataCallback = function(parameter, callbackFunction) {
    commonCallback(parameter, _dataCallback, {}, callbackFunction);
  };
  this.Expend = function(sp) {

    var li = getLiFromSpan(sp);
    if (li.childNodes.length >= 2) {
			if (!_callbackInExpend) {
			  
//				Library.WebControl.Effect.Show({
//					Object: li.childNodes[1],
//					Type: _effectDisplay,
//					Time: _effectTime,
//					Step: _effectStep
//				});     
var divTreeView = li.FindLastChild("div", "sptreeview");
    if (divTreeView != null) {
      Library.WebControl.Effect.Show({
        Object: divTreeView,
        Type: _effectDisplay,
        Time: _effectTime,
        Step: _effectStep
      });
    }     
				
				
				sp.setAttribute("onclick", _clientName + ".Collapse(this);");
				sp.onclick = function() {
					_instance.Collapse(sp);
				};
				_util.SetClass(sp, _expenedClass);
				return;
			} else {
			}	
    }
    var k = li.getAttribute("sptnk");
    var lv = li.getAttribute("sptnl");
    var beforeExpendedEventArgs = {
      Cancel: false,
      Key: k,
      Level: lv
    };
    if (_beforeExpended != null)
			_beforeExpended(_instance, beforeExpendedEventArgs);
    if (beforeExpendedEventArgs.Cancel) {
      return;
    }
    _hashParameter.Add("__SPTREEVIEWPK_", k);
    _hashParameter.Add("__SPTREEVIEWPL_", lv);
    commonCallback("", _expendedCallbackType, {
      Li: li,
      Sp: sp
    }, function(s, e) {
    });

  };
  

  this.AddParameter = function(k, v) {
    _hashParameter.Add(k, v);
  };

  
  this.AddHandler = function(t, f) {
		switch(t) {
			case 1:
				_beforeExpended = f;
				break;
			case 2:
				_beforeCallback = f;
				break;	
		}
  };
};

